`define ADDR_W $clog2(DEPTH)

/**
 * Instruction sram-like interface Request Buffer (circular queue)
 * +-----------+
 * |           |
 * +-----------+
 * |           | <- head (next pop item)
 * +-----------+
 * |           | <- rp (next request response item)
 * +-----------+
 * |           |
 * +-----------+
 * |           | <- tail (empty item)
 * +-----------+
 */

module irb #(
  parameter DEPTH = 4,
  parameter WIDTH = 32
) (
  input clk,
  input rst,
  input flush,
  input br_taken,
  input [31:0] br_ds_pc,

  // write port
  input             wvalid,
  output            wready,
  input [WIDTH-1:0] wdata ,

  // read port (for sram-like request channel)
  output             rvalid,
  input              rready,
  output [WIDTH-1:0] rdata ,

  // pop port (after addr_ok, a request can be poped)
  output             pvalid,
  input              pready,
  output             pcancel,
  output [WIDTH-1:0] pdata 
);
  // when the most signidicant bit of head and tail are equal,
  // the fifo might be empty, or it might be full.
  reg [`ADDR_W:0] head;
  reg [`ADDR_W:0] rp;
  reg [`ADDR_W:0] tail;

  wire empty;
  wire rempty;
  wire full;

  assign empty = head == tail;
  assign rempty = rp == tail;
  assign full = (head[`ADDR_W] ^ tail[`ADDR_W]) && (head[`ADDR_W-1:0] == tail[`ADDR_W-1:0]);

  reg [WIDTH-1:0] fifo [DEPTH-1:0];

  genvar i;
  reg cancel [DEPTH-1:0];

  always@(posedge clk) begin
    if (rst) begin
      tail <= 0;
    end else if (flush && tail != rp) begin
      // flush non-queued requests
      tail <= rp + 1;
    end else if (wvalid && wready && !flush) begin
      tail <= tail + 1;
    end
  end

  always@(posedge clk) begin
    if (rst) begin
      head <= 0;
    end else if (pvalid && pready) begin
      head <= head + 1;
    end
  end    
  
  always@(posedge clk) begin
    if (rst) begin
      rp <= 0;
    end else if (rvalid && rready) begin
      rp <= rp + 1;
    end
  end

  always@(posedge clk) begin
    // avoid overwrite when fifo full
    if (wvalid && wready && !flush) begin
      fifo[tail[`ADDR_W-1:0]] <= wdata;
    end
  end

  generate
    for (i = 0; i < DEPTH; i = i + 1) begin
      always@(posedge clk) begin
        if (rst) begin
          cancel[i] <= 1'b0;
        end else if (flush) begin
          // cancel[i] <= br_taken ? (fifo[i][68:37] != br_ds_pc) : 1'b1;
          cancel[i] <= 1'b1;
        end else if (wvalid && wready && i == tail[`ADDR_W-1:0] && !flush) begin
          cancel[i] <= 1'b0;
        end
      end
    end
  endgenerate

  // stop write when flush valid
  assign wready = ~full;
  
  assign rvalid = ~rempty;
  assign rdata = fifo[rp[`ADDR_W-1:0]];

  // stop pop when flush valid
  // assign pvalid = ~empty & (head != rp) & ~flush;
  assign pvalid = ~empty & (head != rp);
  assign pcancel = cancel[head[`ADDR_W-1:0]];
  assign pdata = fifo[head[`ADDR_W-1:0]];
  
endmodule